grammar plp3;

@header {
	import java.lang.String;
	import java.util.ArrayList;
}

@rulecatch {
	catch (RecognitionException e) {
		reportError(e);
		System.exit(1);
	}
}
@members {
	private TablaSimbolos tSimbolos = new TablaSimbolos(null);

	public boolean mainTrobat=false;
	public clase tb;
	public String locales="";
	public int posicionSimbolo=0;
	public int contSalto = 0;
	public int maxPila = 0;

	public String ponBlancos(int num) {
	        String cad = "";
	        for (int i = 0; i < num * 4; i++) {
	            cad += " ";
       	 	}
        	return cad;
    	}
    	private void ErrorSem1(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 1 ("+fila+","+columna+"): '"+ disfunka +"' ya existe en este ambito");
		System.exit(5);
	}

	private void ErrorSem2(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 2 ("+fila+","+columna+"): simbolo '"+ disfunka +"' no ha sido declarado");
		System.exit(5);
	}

	private void ErrorSem3(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 3 ("+fila+","+columna+"): tipo incompatible en operador aritmetico '"+ disfunka +"'");
		System.exit(5);
	}

	private void ErrorSem4(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 4 ("+fila+","+columna+"): tipo incompatible en operador logico '"+ disfunka +"'");
		System.exit(5);
	}

	private void ErrorSem5(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 5 ("+fila+","+columna+"): la expresion debe ser de tipo booleano en la instruccion '"+ disfunka +"'");
		System.exit(5);
	}

	private void ErrorSem6(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 6 ("+fila+","+columna+"): tipos incompatibles en la instruccion de asignacion");
		System.exit(5);
	}

	private void ErrorSem7(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 7 ("+fila+","+columna+"): tipos incompatibles en la instruccion de lectura");
		System.exit(5);
	}

	private void ErrorSem8(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 8 ("+fila+","+columna+"): tamanyo incorrecto");
		System.exit(5);
	}
	private void ErrorSem9(int fila, int columna, String disfunka)
	{
		System.err.println("Error 9 ("+fila+","+columna+"): numero insuficiente de indices en el array '"+ disfunka +"'");
		System.exit(5);
	}


	private void ErrorSem10(int fila, int columna, String disfunka)
	{
		System.err.println("Error 10 ("+fila+","+columna+"): numero de dimensiones incorrecto");
		System.exit(5);
	}

	private void ErrorSem11(int fila, int columna, String disfunka)
	{
		System.err.println("Error 11 ("+fila+","+columna+"): el identificador '"+ disfunka +"' no es de tipo array");
		System.exit(5);
	}

	private void ErrorSem12(int fila, int columna, String disfunka)
	{
		System.err.println("Error 12 ("+fila+","+columna+"): demasiados indices");
		System.exit(5);
	}

	private void ErrorSem13(int fila, int columna)
	{
		System.err.println("Error 13 ("+fila+","+columna+"): indice de tipo incompatible");
		System.exit(5);
	}

	private void ErrorSem14(int fila, int columna, String disfunka)
	{
		System.err.println("Error 14 ("+fila+","+columna+"): tipo '" + disfunka +"' incompatible con la declaracion");
		System.exit(5);
	}

	private void ErrorSem15(int fila, int columna, String disfunka)
	{
		System.err.println("Error 15 ("+fila+","+columna+"): la variable que se intenta modificar es una variable indice");
		System.exit(5);
	}

	private void ErrorSem16(int fila, int columna, String disfunka)
	{
		System.err.println("Error 16 ("+fila+","+columna+"): instruccion '" + disfunka +"' no permitida fuera de un bucle");
		System.exit(5);
	}

	private void ErrorSem17(int fila, int columna, String disfunka)
	{
		System.err.println("Error 17 ("+fila+","+columna+"): la expresion debe ser de tipo numerico");
		System.exit(5);
	}

	public void emitErrorMessage(String mensaje)
	{
		System.err.println(mensaje);
		System.exit(1);
	}


}

@lexer::members {
	public void emitErrorMessage(String mensaje)
	{
		System.err.println(mensaje);
		System.exit(1);
	}
}


s [String entrada] returns [String eixida]
	@init{
		$eixida= "";
	}:
	{
		$eixida+= ".assembly extern mscorlib{}\n" +
			  ".assembly '" + entrada +"' {}\n";
	}
	clase{
		$eixida += $clase.eixida;
	}
	;


clase returns [String eixida]
	@init{
		$eixida= "";
	}:
	CLASS SINGLE LLAVEI {
		$eixida+= ".class 'Single' extends [mscorlib]System.Object {\n" +
			".method public specialname rtspecialname instance void .ctor() cil managed\n{"+
			".maxstack 1\nldarg.0\ncall instance void [mscorlib]System.Object::.ctor()\nret\n}\n";
		tSimbolos.nuevoSimbolo("Single",Simbolo.CLASE,0,0);



	}
	metodo
	LLAVED {
		$eixida += $metodo.eixida+ "}\n";
	}
	;

metodo returns [String eixida]
	@init{
		$eixida= "";
	}:
	PUBLIC STATIC VOID MAIN PARI PARD
	bloque{
		if (locales.length()>0)
		{
			locales = locales.substring(0,locales.length()-2);
		}
		$eixida+=".method static public void main () cil managed\n{\n.entrypoint\n.maxstack " +
		($bloque.maxPila+15) +"\n.locals(" + locales + ")\n" + $bloque.eixida +"ret\n}\n";
	};

tiposimple returns [String eixida, int tipo, int linea, int posicion] // linea y posicion los usamos en new TipoSimple
	@init{
		$eixida= "";
	}:
	INT{
		$tipo = Simbolo.ENTERO;
		$linea = $INT.line;
		$posicion = $INT.pos;
		$eixida+= $INT.text;
	}
	| DOUBLE{
		$tipo = Simbolo.REAL;
		$linea = $DOUBLE.line;
		$posicion = $DOUBLE.pos;
		$eixida+= $DOUBLE.text;
	}
	| BOOL{
		$tipo = Simbolo.BOOLEANO;
		$linea = $BOOL.line;
		$posicion = $BOOL.pos;
		$eixida+= $BOOL.text;
	};

decl returns [String eixida, int maxPila]
	@init{
		$eixida= "";
		$maxPila = 0;
	}:
	tiposimple
	v1= varid [$tiposimple.tipo]{
		$eixida+= $v1.eixida;
		$maxPila = Math.max($maxPila, $v1.maxPila);
	}

	(
	COMA
	v2= varid [$tiposimple.tipo]{
		$eixida += $v2.eixida;
		$maxPila = Math.max($maxPila, $v2.maxPila);
	}
	)*
	PYC
	;
varid [int tipo] returns [String eixida, int maxPila]
	@init{
		int contElementos=0;
		$eixida= "";
		$maxPila=0;
	}:
	ID{
		if(tSimbolos.existe($ID.text)){
			ErrorSem1($ID.line,$ID.pos,$ID.text);
		}
		if 	( $tipo==Simbolo.ENTERO)  locales += "int32";
		else if ( $tipo==Simbolo.REAL) locales += "float64";
		else 	locales += "int32";
	}
	(CORI
	{// Registramos que es un vector
		contElementos++;
	}
	(COMA
	{
	// cantidadElementos++
		contElementos++;
	}
	)*
	CORD
	{
		locales += " []";
		$maxPila = 1;
	})?
	{
		tSimbolos.nuevoSimbolo($ID.text,Simbolo.VAR,$tipo,posicionSimbolo++,contElementos);
		locales += ", ";
	}
	;

instr[int saltoinicio, int saltofinal] returns [String eixida, boolean vieneDeBreakOContinue, int maxPila]
	@init{
		//bool conElse = false;
		//bool conSigno= false;

		boolean conElse = false;
		boolean conSigno= false;
		int indice= 0;
		int tipo=0;
		int saltaux=0;
		int saltaux2=0;
		int saltaux3=0;
		Simbolo simbaux= null;
		$eixida="";
		$vieneDeBreakOContinue= false;
		$maxPila=0;

	}:
	bloque {
		$eixida += $bloque.eixida;
		$maxPila = $bloque.maxPila;
	}
	|


	IF PARI
	expr{
		$eixida += $expr.eixida;
		$eixida += "ldc.i4 0\n";
		$eixida += "bne.un L" + contSalto +"\n";
		$eixida += "br L" + (contSalto+1) +"\n";
		saltaux= contSalto;
		contSalto+=3;

		$maxPila = Math.max($expr.maxPila, 3);
	}
	PARD
	i1= instr[0,0]
	{
		$eixida +="L" + saltaux +":\n";
		$eixida +=$i1.eixida;
		$eixida +="br L" + (saltaux+2)+"\n";
		$maxPila = Math.max($maxPila, $i1.maxPila);
	}
	(ELSE i2=instr[0,0]{
	 	conElse= true;
		$eixida +="L" + (saltaux+1) +":\n";
		$eixida += $i2.eixida;
	}
	)?{
		if(!conElse)
		{
			$eixida +="L" + (saltaux+1) +":\n";
			$maxPila = Math.max($maxPila, $i2.maxPila);
		}
		$eixida += "L" + (saltaux+2) +":\n";
	}

	|

	WHILE PARI
	{
		saltaux= contSalto;
		$eixida += "L"+ contSalto++ + ":\n";
	}
	expr
	{
		if($expr.tipo!=Simbolo.BOOLEANO)
		{
			ErrorSem5($WHILE.line,$WHILE.pos,"while");
		}
		$eixida += $expr.eixida;  //adimos el cod de la condici, que deja un 1 o un 0 en la pila
		$eixida += "ldc.i4 0\n";	// apilamos 0 para comparar
		$eixida += "bne.un L"+ (contSalto+1) + "\n";	// si no son iguales, al codigo de la instruccion
		$eixida += "br L"+ (contSalto+2) + "\n";	// iguales, salgo del bucle
		saltaux2= contSalto;
		contSalto+=3;

		$maxPila = Math.max(3, $expr.maxPila);
	}
	PARD
	i3= instr[saltaux,saltaux2+2]
	{
		saltaux3= contSalto;
		contSalto= saltaux2;
		$eixida += "L"+ (contSalto+1) + ":\n";
		$eixida += $i3.eixida;
		$eixida += "br L"+ saltaux + "\n";
		$eixida += "L"+ (contSalto+2)  +":\n";
		contSalto = saltaux3;

		$maxPila = Math.max($maxPila, $i3.maxPila);
	}
	|
	FOREACH PARI{
		tSimbolos = new TablaSimbolos(tSimbolos);
	} VAR id1 = ID
	{
		// ponemos a 0 porque es el indice que recorre el vector
		tSimbolos.nuevoSimbolo($id1.text,Simbolo.VAR,Simbolo.ENTERO,posicionSimbolo++,true);
		$eixida += "ldc.i4 0\n";
		$eixida += "stloc " + (posicionSimbolo-1) +"\n"; // inicializo var a 0
		saltaux = contSalto++;
		$eixida += "L"+ saltaux +":\n";  // etiqueta para el comienzo del bucle

		$maxPila = 3;

	} IN id2 = ID
	{
		simbaux = tSimbolos.busca($id2.text);
           	if(simbaux == null){
           		ErrorSem2($id2.line,$id2.pos,$id2.text); // no ha sido declarado
           	}
          	$eixida += "ldc.i4 "+ String.valueOf(simbaux.getDimParcial(0)) +"\n"; // devuelve el tamanyo del vector
           	$eixida += "ldloc " + (posicionSimbolo-1) +"\n";		// en la pila el indice
           	saltaux2 = contSalto++;
           	$eixida += "bgt L"+ saltaux2 +"\n"; // Si el tope el mayor que el indice, salta a la instr
           	saltaux3 = contSalto++;
           	$eixida += "bgt L"+ saltaux3 +"\n"; // si no, salimos del bucle

           	$maxPila = Math.max ($maxPila, 4);

	} PARD
	i4=instr[saltaux,saltaux3]{
		$eixida += "L"+ saltaux2 +":\n";


		// Actualizamos el indice
		$eixida += "ldloc " + (posicionSimbolo-1) +"\n";
		$eixida += "ldc.i4 "+ String.valueOf(simbaux.getDimParcial(0))+"\n";
		$eixida+="add\n";
		$eixida+= "stloc " + (posicionSimbolo-1) +"\n";
		$eixida+= "br L" + (saltaux) +"\n";

		$eixida += $i4.eixida;

		// Salida del bucle
		$eixida+= "L" + saltaux3 +":\n";
		//emitErrorMessage("CATAPLASMA");
		simbaux.quitaIndice();
		tSimbolos = tSimbolos.pop();
		$maxPila = Math.max ($maxPila, $i4.maxPila);
	}

	|
	FOR PARI{

		tSimbolos = new TablaSimbolos(tSimbolos);
	}
	 INT ID ASIG
	e1=expr{
			tSimbolos.nuevoSimbolo($ID.text,Simbolo.VAR,Simbolo.ENTERO,posicionSimbolo++,true);
			$eixida += $e1.eixida;
			$eixida += "stloc " + (posicionSimbolo-1) +"\n";
			$maxPila = $e1.maxPila;

	}
	TO
	e2=expr{
		//emitErrorMessage(((Integer)Simbolo.ENTERO).toString() + " " + ((Integer)e2.tipo).toString());


		if($e2.tipo!=Simbolo.ENTERO && $e2.tipo!=Simbolo.REAL){
			ErrorSem17($TO.line,$TO.pos,$TO.text);
		}
		$eixida += $e2.eixida;
		//En teoria, en cada iteraciob el resultado de la expresion aparecera antes de la comparacion
		//en la cima de la pila. Si esto no funcionara, tendremos que almacenarlo en una variable
		//y cargarlo justo cuando cargamos el valor del simbolo indice.
		saltaux = contSalto;
		$eixida += "L"+ contSalto++ + ":\n";
		$eixida += "ldloc " + (posicionSimbolo-1) +"\n";

	}
	STEP (ADDOP{conSigno= true;})? ENTERO PARD{
		saltaux2= contSalto;
		if(conSigno && $ADDOP.text.equals("-")){
			$eixida += "ble L"+ contSalto++ +"\n";
		} else {
			$eixida += "bge L"+ contSalto++ +"\n";
		}
		$eixida += "br L" + contSalto++ +"\n";
		$maxPila = Math.max ($maxPila, $e2.maxPila+3);
	}
	i5=instr[saltaux,saltaux2+1]{

		$eixida += "L" + (saltaux2) +":\n";
		$eixida += $i5.eixida;
		$eixida += "ldloc " + (posicionSimbolo-1) +"\n";
		$eixida += "ldc.i4 "+ $ENTERO.text+"\n";
		if(conSigno && $ADDOP.text.equals("-")){
			$eixida+="sub\n";
		} else {
			$eixida+="add\n";
		}
		$eixida+= "stloc " + (posicionSimbolo-1) +"\n";
		$eixida+= "br L" + (saltaux) +"\n";
		$eixida+= "L" + (saltaux2+1) +":\n";
		tSimbolos = tSimbolos.pop();
		$maxPila = Math.max ($maxPila, $i5.maxPila);
	}

	|

	BREAK PYC{
		if($saltoinicio==0&&$saltofinal==0){
			ErrorSem16($BREAK.line,$BREAK.pos+1,$BREAK.text);
		}
		$eixida+= "br L"+saltofinal+"\n";
	}
	|

	CONTINUE PYC{
		if($saltoinicio==0&&$saltofinal==0){
			ErrorSem16($CONTINUE.line,$CONTINUE.pos+1,$CONTINUE.text);
		}
		$eixida+= "br L"+saltoinicio+"\n";
	}
	|
	ref
	{
		if($ref.vector)// NO puedes usar lo de Simbolo.ARRAY,
		{
			$eixida+="ldloc "+$ref.eixida;

			// quedando
			// ldloc [posicion del array en locals]
			// ldl.i4 [posicion del elemento dentro del array]
			$maxPila = 1;
		}
	}
	cambio[$ref.tipo,$ref.esindice]	// dentro de cambio se comprueba el tipo
	{
		// Esto es lo que ha puesto Angel
		$eixida +=$cambio.eixida;
		if(!$ref.vector)
		{
			$eixida += "stloc "+ $ref.eixida + "\n";
		}
		else
		{

			if($ref.tipo == Simbolo.REAL )
			{
				// Si lo que se devuelve en "cambio" es un real, hay que convertirlo

				$eixida += "stelem.r8\n";
			}
			else
			{

				$eixida += "stelem.i4\n";
			}
		}

		$maxPila = 4;
	}
	|

	ID
	{
		simbaux = tSimbolos.busca($ID.text);
		if(simbaux == null){
           		ErrorSem2($ID.line,$ID.pos,$ID.text); // no ha sido declarado
           	} else {

           		if(!simbaux.esArray())
           		{
           			ErrorSem11($ID.line,$ID.pos+1,$ID.text); // no es un array
           		}
           		tipo =simbaux.tipo;
           	}
	}
	ASIG NEW
	tiposimple
	{
		if (tipo != $tiposimple.tipo)
		{
			ErrorSem14($tiposimple.linea,$tiposimple.posicion+1,$tiposimple.eixida); // no es el tiposimple que corresponde
		}
	}
	CORI
	dims[simbaux]
	CORD
	{
		if (!simbaux.setOrra($dims.tamanyos))	// insertamos las dimensiones en el simbolo
		{
		ErrorSem10($CORD.line,$CORD.pos+1,$CORD.text);// faltan dimensiones
		}
		// generar
		// ldc.i4 $dims.dimensionTotal
		// newarr[mscorlib] System. [Double/int32]
		// stloc simbaux.getPosicion()
		$eixida += "ldc.i4 "+ $dims.dimensionTotal + "\n";
		$eixida += "newarr[mscorlib] System.";
		if (tipo == Simbolo.REAL)
		{
			$eixida +="Double\n";
		}else
		{
			$eixida +="Int32\n";
		}
		$eixida += "stloc "+ String.valueOf(simbaux.getPosicion())+"\n";
		$maxPila = 4;
	}
	PYC
	|

	WRITELINE PARI
	expr {
		$eixida+= $expr.eixida;
		$eixida += "call void [mscorlib]System.Console::WriteLine(";
		if($expr.tipo==(Simbolo.ENTERO)) $eixida+= "int32)\n";
		else if($expr.tipo==(Simbolo.REAL)) $eixida+= "float64)\n";
		else if($expr.tipo==(Simbolo.BOOLEANO)) $eixida+="bool)\n";

		$maxPila = $expr.maxPila+1;
	}
	PARD PYC
	;



bloque returns [String eixida, int tampila, int maxPila]
	@init{
		$eixida = "";
	}:
	LLAVEI{
		tSimbolos = new TablaSimbolos(tSimbolos);
	}
	declins {
		$eixida += $declins.eixida;
		$maxPila = $declins.maxPila;
	}
	LLAVED {
		tSimbolos = tSimbolos.pop();
	}
	;

declins returns [String eixida, int maxPila]
	@init{
		$eixida= "";
		$maxPila = 0;
	}:
	(
	instr[0,0]{
		$eixida += $instr.eixida;
		$maxPila = Math.max($maxPila, $instr.maxPila);
	}
	|
	decl{
		$eixida += $decl.eixida;
		$maxPila = Math.max($maxPila, $decl.maxPila);
	}
	)*
	;

dims[Simbolo sim] returns [String eixida, ArrayList tamanyos, int dimensionTotal]
	@init
	{

		int tamanyoDim=0; //  tamanyo de cada dimension
		int cont =0; // dimensiones que vamos anyadiendo
		int tamArray=sim.getTamanyoArray();
	}:
	{
		$eixida="";
	}
	e1=ENTERO
	{
		$tamanyos = new ArrayList();

		tamanyoDim = Integer.parseInt($e1.text);
		if (tamanyoDim <= 0)
		{
			ErrorSem8($e1.line,$e1.pos,$e1.text);// la dimension no es mayor que 0
		}
		$tamanyos.add(tamanyoDim);
		cont++;
		$dimensionTotal = tamanyoDim;
	}
	(COMA e2=ENTERO
	{
		tamanyoDim = Integer.parseInt($e2.text);
		$dimensionTotal = $dimensionTotal * tamanyoDim;
		if (cont >= tamArray) // nos hemos pasado con las dimensiones
		{
			ErrorSem10($COMA.line,$COMA.pos+1,$COMA.text);
		}
		if (tamanyoDim <= 0)
		{
			ErrorSem8($e2.line,$e2.pos,$e2.text);
		}
		$tamanyos.add(tamanyoDim);
		cont++;

	})*
	;


cambio[int tipo,boolean esindice] returns [String eixida]
	@init{
		$eixida= "";
	}:
	ASIG{

		if($esindice){
			ErrorSem15($ASIG.line,$ASIG.pos+1,"He bebido mas cerveza, he meado mas sangre, he echado mas polvos y he chafado mas huevos que todos vosotros juntos, capullos.");
		}
	}
	expr{
		if($tipo!=$expr.tipo && ($tipo==(Simbolo.BOOLEANO) || $expr.tipo==(Simbolo.BOOLEANO)))
		{
			ErrorSem6($ASIG.line,$ASIG.pos,$ASIG.text);
		}
	}
	PYC {

		if($tipo==(Simbolo.REAL) && $expr.tipo==(Simbolo.ENTERO))
		{
			$eixida += $expr.eixida + "conv.r8\n";

		} else if ($tipo==(Simbolo.ENTERO) && $expr.tipo==(Simbolo.REAL)){
			$eixida += $expr.eixida + "conv.i4\n";

		} else {
			$eixida += $expr.eixida;
		}
	}
	|
	PUNTO READLINE{
		if($esindice){
			ErrorSem15($READLINE.line,$READLINE.pos+1,"Tengo tres piscinas");
		}
		} PYC{
		$eixida += "call string [mscorlib]System.Console::ReadLine()\n";
		if($READLINE.text.contains("int")){
			if($tipo!=Simbolo.ENTERO) {
				ErrorSem7($READLINE.line,$READLINE.pos,$READLINE.text);
			} else {
				$eixida += "call int32 [mscorlib]System.Int32::Parse(string)\n";
			}
		} else if($READLINE.text.contains("double")){
			if($tipo!=Simbolo.REAL) {
				ErrorSem7($READLINE.line,$READLINE.pos,$READLINE.text);
			} else {
				$eixida += "call float64 [mscorlib]System.Double::Parse(string)\n";
			}
		} else if($READLINE.text.contains("bool")){
			if($tipo!=Simbolo.BOOLEANO) {
				ErrorSem7($READLINE.line,$READLINE.pos,$READLINE.text);
			} else {
				$eixida += "call bool [mscorlib]System.Boolean::Parse(string)\n";
			}
		}

	}
	;

expr returns [String eixida, int tipo, boolean signo, int maxPila]
	@init{
		boolean signo2;
		int tipo2;
		String eixida2;
		$eixida="";
	}:
        ea1=eand{
		$signo = $ea1.signo;
		$tipo = $ea1.tipo;
		$eixida += $ea1.eixida;
		$maxPila=$ea1.maxPila;
        }
        (OR
        ea2= eand {
		signo2 = $ea2.signo;
		tipo2 = $ea2.tipo;
		eixida2 = $ea2.eixida;

		if($tipo!=Simbolo.BOOLEANO || tipo2!=Simbolo.BOOLEANO)
		{
			ErrorSem4($OR.line,$OR.pos,$OR.text);
		}
		$eixida += eixida2;
		$eixida += "or\n";
		$maxPila=Math.max($maxPila,$ea2.maxPila+1);
        }
        )*
    ;

eand returns [String eixida, boolean signo, int tipo, int maxPila]
	@init{
		boolean signo2;
		int tipo2;
		String eixida2;
		$eixida ="";
	}:
        e1=erel{
		$signo = $e1.signo;
		$tipo = $e1.tipo;
		$eixida += $e1.eixida;
		$maxPila=$e1.maxPila;
        }
        (AND
        e2= erel {
		signo2 = $e2.signo;
		tipo2 = $e2.tipo;
		eixida2 = $e2.eixida;
		if($tipo!=Simbolo.BOOLEANO || tipo2!=Simbolo.BOOLEANO)
		{
			ErrorSem4($AND.line,$AND.pos,$AND.text);
		}
		$eixida += eixida2;
		$eixida += "and\n";
		$maxPila=Math.max($maxPila,$e2.maxPila+1);
        }
        )*
    ;
erel returns [String eixida, boolean signo, int tipo, int maxPila]
	@init{
		boolean signo2;
		int tipo2;
		String eixida2;
		$eixida ="";
	}:
        e1=esum{
		$signo = $e1.signo;
		$tipo = $e1.tipo;
		$eixida += $e1.eixida;
		$maxPila = $e1.maxPila;
        }
        (RELOP
        e2=esum {
		signo2 = $e2.signo;
		tipo2 = $e2.tipo;
		eixida2 = $e2.eixida;
		if($tipo==(Simbolo.REAL) && (tipo2==(Simbolo.ENTERO) || tipo2==(Simbolo.BOOLEANO)))
		{
			eixida2 += "conv.r8\n";

		} else if (($tipo==(Simbolo.ENTERO) || $tipo==(Simbolo.BOOLEANO)) && tipo2==(Simbolo.REAL)){
			$eixida += "conv.r8\n";
		}
		$eixida += eixida2;
		$tipo=Simbolo.BOOLEANO;
		if($RELOP.text.equals("==")){
			$eixida += "beq ";
		} else if($RELOP.text.equals("!=")){
			$eixida += "bne.un ";
		} else if($RELOP.text.equals("<")){
			$eixida += "blt ";
		} else if($RELOP.text.equals(">")){
			$eixida += "bgt ";
		} else if($RELOP.text.equals("<=")){
			$eixida += "ble ";
		} else {
			$eixida += "bge ";
		} $eixida += "L" + contSalto++ +"\n";
		$eixida +="ldc.i4 0\nbr L" + contSalto-- + "\nL"+ contSalto++ + ":\nldc.i4 1\nL"+contSalto++ +":\n";

		$maxPila=Math.max($maxPila,$e2.maxPila+1);
        }
        )*
    ;

esum    returns [boolean signo, int tipo, String eixida, int maxPila]
	@init{

		boolean signo2;
		int tipo2;
		String eixida2;
		$eixida ="";
		$signo= true;
	}:
        costra=term{
		$signo = $costra.signo;
		$tipo = $costra.tipo;
		$eixida += $costra.eixida;
		$maxPila = $costra.maxPila;

        }
        (ADDOP
        seca=term {
        	if($tipo==(Simbolo.BOOLEANO)){
			ErrorSem3($ADDOP.line,$ADDOP.pos,$ADDOP.text);
		}
		signo2 = $seca.signo;
		tipo2 = $seca.tipo;
		eixida2 = $seca.eixida;
		if(tipo2==(Simbolo.BOOLEANO)){
			ErrorSem3($ADDOP.line,$ADDOP.pos,$ADDOP.text);
		}
		if($tipo==(Simbolo.REAL) && tipo2==(Simbolo.ENTERO))
		{
			eixida2 += "conv.r8\n";

		} else if ($tipo==(Simbolo.ENTERO) && tipo2==(Simbolo.REAL)){
			$eixida += "conv.r8\n";
		}
		$eixida += eixida2;
		if($ADDOP.text.equals("+")){
			$eixida += "add\n";
		} else {
			$eixida += "sub\n";
		}
		$maxPila=Math.max($maxPila,$seca.maxPila+1);
        }
        )*
    ;

term    returns [boolean signo, int tipo, String eixida, int maxPila]
	@init{
		boolean signo2;
		int tipo2;
		String eixida2;
		$eixida="";
	}:
        max=factor{
		$signo = $max.signo;
		$tipo = $max.tipo;
		$eixida += $max.eixida;
		$maxPila = $max.maxPila;
        }
        (MULOP
        pollotriste=factor {
        	if($tipo==(Simbolo.BOOLEANO)){
			ErrorSem3($MULOP.line,$MULOP.pos,$MULOP.text);
		}
		signo2 = $pollotriste.signo;
		tipo2 = $pollotriste.tipo;
		eixida2 = $pollotriste.eixida;
		if(tipo2==(Simbolo.BOOLEANO)){
			ErrorSem3($MULOP.line,$MULOP.pos,$MULOP.text);
		}
		if($tipo==(Simbolo.REAL) && tipo2==(Simbolo.ENTERO))
		{
			eixida2 += "conv.r8\n";

		} else if ($tipo==(Simbolo.ENTERO) && tipo2==(Simbolo.REAL)){
			$eixida += "conv.r8\n";
		}
		$eixida += eixida2;
		if($MULOP.text.equals("*")){
			$eixida += "mul\n";
		} else {
			$eixida += "div\n";
		}
		$maxPila=Math.max($maxPila,$pollotriste.maxPila);
        }
        )*
    ;

factor   returns [boolean signo, int tipo, String eixida, int maxPila]
	@init{
		$eixida= "";
		$maxPila=1;
	}:
        base{
		$signo = $base.signo;
		$tipo = $base.tipo;
		$eixida += $base.eixida;
		$maxPila = $base.maxPila;
        }
    	| ouiea=NOT xopo=factor{
    		$signo = $xopo.signo;
    		$tipo = $xopo.tipo;
    		$eixida += $xopo.eixida;
    		if($tipo!=Simbolo.BOOLEANO)
    		{
    			ErrorSem4($ouiea.line,$ouiea.pos,$ouiea.text);
    		} else {
    			$signo = !$signo;
    			$eixida += "not\n";

    		}
    		$maxPila = $xopo.maxPila;
        }
        | PARI adios=ADDOP loco=factor{
            	$signo = $loco.signo;
    		$tipo = $loco.tipo;
    		$eixida += $loco.eixida;
    		if($tipo==(Simbolo.BOOLEANO))
    		{
    			ErrorSem3($adios.line,$adios.pos,$adios.text);
    		} else {
    			$signo = !$signo;
    			$eixida+="neg\n";
    		}
    		$maxPila = $loco.maxPila;
        }
        PARD
    ;

base    returns [boolean signo, int tipo, String eixida, int maxPila]
	@init{
		$eixida= "";
		$signo = true;
	}:
         ENTERO {
         	$eixida += "ldc.i4 "+ $ENTERO.text +"\n";
         	$tipo = Simbolo.ENTERO;
         	$maxPila = 1;
         }
         | REAL {
         	$eixida += "ldc.r8 "+ $REAL.text +"\n";
         	$tipo = Simbolo.REAL;
         	$maxPila = 1;
         }
         | BOOLEANO {
         	if($BOOLEANO.text.equals("True")){
         		$eixida += "ldc.i4.1\n";
         	} else {
         		$eixida += "ldc.i4.0\n";
         	}
         	$tipo = Simbolo.BOOLEANO;
         	$maxPila = 1;

         }

    	| PARI expr{
    		$eixida += $expr.eixida;
    		$tipo = $expr.tipo;
    		$signo = $expr.signo;
    		$maxPila = $expr.maxPila;
        }
        PARD
            	|
            	ref
            	{
    		$eixida += "ldloc "+ $ref.eixida;
    		$tipo = $ref.tipo;
    		$signo = $ref.signo;

    		if ($ref.vector)
    		{
	    		if($ref.tipo == Simbolo.REAL )
			{
				$eixida += "ldelem.r8\n";
			}
			else
			{
				$eixida += "ldelem.i4\n";
			}
		}
		$maxPila=Math.max(3,$ref.maxPila);

        }
    ;

// puede ser llamado para lectura (desde Base) o para escritura (desde Instr->ref cambio) sunicamente devuelve la posicion de la ref en locals
ref returns[String eixida, int tipo, boolean signo, boolean vector, boolean esindice, int maxPila]
	@init{
		Simbolo simbaux= null;
		boolean corchetes = true;
		$eixida= "";
	}:
            ID{
           	simbaux = tSimbolos.busca($ID.text);
           	if(simbaux == null){
           		ErrorSem2($ID.line,$ID.pos,$ID.text); // no ha sido declarado
           	} else {
			if(simbaux.esIndice()) $esindice=true;
           		$eixida += String.valueOf(simbaux.getPosicion()) + "\n"; // devuelve la posicion en locales
           		$tipo =simbaux.tipo;
           	}
		$maxPila = 1;
           }
          (CORI
           {
           	if(!simbaux.esArray())
           	{
           		ErrorSem11($ID.line,$ID.pos+1,$ID.text); // no es un array, no deberia estar aqui
           	}
           	$vector = true;
           }
           indices[simbaux,$CORI.line,$CORI.pos]
           {
           	$eixida +=$indices.eixida;//+posicion dentro del array
           	$maxPila = Math.max( $indices.maxPila,$maxPila);
           }
           CORD)?
           {
           	// Si siendo un array no tiene corchetes o hay un nimero menor de indices que de dimensiones, Error9
           	if(simbaux.esArray() && (!corchetes || $indices.cont < simbaux.getTamanyoArray() ))
           	{
           		ErrorSem9($ID.line,$ID.pos+1,$ID.text);
           	}
           }
           ;
indices[Simbolo sim, int linea, int columna] returns[String eixida,int cont, int indicesPos, int maxPila]
	@init{
		$eixida="";
		$cont = 0;
	}:
	e1 = expr{
		$eixida += "ldc.i4 0\n";// cargo un 0 para la primera suma
		$eixida += $e1.eixida;

        	if ($e1.tipo==Simbolo.REAL)
        	{
        		$eixida += "conv.i4 "+ "\n";
        	}
        	else
        	if ($e1.tipo!=Simbolo.ENTERO)
        	{
        		ErrorSem13(linea,columna);// indice de tipo incompatible
        	}
        	$cont++;
        	$eixida += "ldc.i4 "+ $sim.getDimParcial($cont) +"\n";
        	$eixida += "mul\n";
        	$eixida += "add\n";
		$maxPila = Math.max( $e1.maxPila,3);
        }
        (COMA
        e2 = expr{
        	$eixida += $e2.eixida;
        	if ($e2.tipo==Simbolo.REAL)
        	{
        		$eixida += "conv.i4 "+ "\n";
        	}
        	if ($e2.tipo!=Simbolo.ENTERO)
        	{
        		ErrorSem13($COMA.line,$COMA.pos+1);// indice de tipo incompatible
        	}
        	$cont++;
        	if ($cont > $sim.getTamanyoArray()) // hay mas indices de que los que soporta el array
        	{
        		ErrorSem12($COMA.line,$COMA.pos+1,$COMA.text);
        	}

        	$eixida += "ldc.i4 "+ $sim.getDimParcial($cont) +"\n";
        	$eixida += "mul\n";
        	$eixida += "add\n";
        	$maxPila = Math.max( $e2.maxPila,3);
        }
        )*
	;



NEWline          : ('\r'? '\n'|' '|'\t')+  { skip(); };
COMENTARIO       : '/*' .* '*/' {skip();};
COMENTARIOSIMPLE
	:'//' ~('\n'|'\r')* '\r'? '\n' {skip();};
PYC     :';' ;
ASIG    :'=' ;

MAIN 	:'Main';

CLASS   :'class' ;
SINGLE	:'Single';
DOUBLE  :'double';
INT     :'int' ;
BOOL	:'bool';
VOID    :'void';
PUBLIC	:'public';
STATIC	:'static';

IF	:'if';
ELSE	:'else';
FOREACH	:'foreach';
VAR	:'var';
IN	:'in';

FOR	:'for';
TO	:'to';
STEP	:'step';
WHILE	:'while';
BREAK	:'break';
CONTINUE:'continue';
NEW	:'new';
WRITELINE
	:'System.Console.WriteLine';
READLINE:('int'|'double'|'bool')'.Parse(System.Console.ReadLine())';
PARI    :'(' ;
PARD    :')' ;
LLAVEI  :'{' ;
LLAVED  :'}' ;
CORI	:'[';
CORD	:']';
COMA	:',';
OR	:'|';
AND	:'&';
RELOP	:'=='|'!='|'<'|'>'|'<='|'>=';
ADDOP	:'+'|'-';
MULOP	:'*'|'/';
NOT	:'!';
PUNTO	:'.';
ENTERO  :('0'..'9')+ ;
REAL	:('0'..'9')+'.'('0'..'9')+ ;
BOOLEANO:'True'|'False';
ID      :('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')*;